//------------------------------------------------------------
// shaco Framework
// Copyright © 2017-2021 chang.liu All rights reserved.
// Feedback: 449612236@qq.com
//------------------------------------------------------------

using System.Collections;
using System.IO;
using System.Text;
using System;

namespace shaco.Base
{
    public class DataBuffer
    {
        private MemoryStream _stream = null;
        private BinaryWriter _writer = null;
        private BinaryReader _reader = null;

        public DataBuffer(byte[] data = null)
        {
            if (data != null)
            {
                _stream = new MemoryStream(data);
            }
            else
            {
                _stream = new MemoryStream();
            }
            _reader = new BinaryReader(_stream);
            _writer = new BinaryWriter(_stream);
        }

        public void Close()
        {
            if (_writer != null) _writer.Close();
            if (_reader != null) _reader.Close();
            if (_stream != null) _stream.Close();

            _writer = null;
            _reader = null;
            _stream = null;
        }

        public void Seek(int offset, SeekOrigin loc)
        {
            _stream.Seek(offset, loc);
        }

        public void SetLength(int len = 0)
        {
            _stream.SetLength(len);
        }

        public int RemainLen()
        {
            return (int)(_stream.Length - _stream.Position);
        }

        public int GetLength()
        {
            return (int)_stream.Length;
        }

        public void ResetRemain()
        {
            byte[] leftover = _reader.ReadBytes(RemainLen());
            _stream.SetLength(0);
            _stream.Write(leftover, 0, leftover.Length);
            _stream.Seek(0, SeekOrigin.Begin);
        }

        public void WriteByte(byte v)
        {
            _writer.Write(v);
        }

        public byte ReadByte()
        {
            byte retValue = 0;
            try
            {
                retValue = _reader.ReadByte();
            }
            catch (System.Exception e)
            {
                LogError("DataBuffer ReadByte error", e);
            }
            return retValue;
        }

        public void WriteInt(int v)
        {
            _writer.Write((int)v);
        }

        public int ReadInt()
        {
            int retValue = 0;
            try
            {
                retValue = (int)_reader.ReadInt32();
            }
            catch (System.Exception e)
            {
                LogError("DataBuffer ReadInt error", e);
            }
            return retValue;
        }

        public void WriteUInt(uint v)
        {
            _writer.Write((uint)v);
        }

        public uint ReadUInt()
        {
            uint retValue = 0;
            try
            {
                retValue = _reader.ReadUInt32();
            }
            catch (System.Exception e)
            {
                LogError("DataBuffer ReadUInt error", e);
            }
            return retValue;
        }

        public void WriteLong(long v)
        {
            _writer.Write((long)v);
        }

        public long ReadLong()
        {
            long retValue = 0;
            try
            {
                retValue = (long)_reader.ReadInt64();
            }
            catch (System.Exception e)
            {
                LogError("DataBuffer ReadLong error", e);
            }
            return retValue;
        }

        public void WriteUShort(ushort v)
        {
            _writer.Write((ushort)v);
        }

        public ushort ReadUShort()
        {
            ushort retValue = 0;
            try
            {
                retValue = (ushort)_reader.ReadUInt16();
            }
            catch (System.Exception e)
            {
                LogError("DataBuffer ReadUShort error", e);
            }
            return retValue;
        }

        public void WriteShort(short v)
        {
            _writer.Write((short)v);
        }

        public short ReadShort()
        {
            short retValue = 0;
            try
            {
                retValue = (short)_reader.ReadInt16();
            }
            catch (System.Exception e)
            {
                LogError("DataBuffer ReadShort error", e);
            }
            return retValue;
        }

        public void WriteFloat(float v)
        {
            byte[] temp = BitConverter.GetBytes(v);
            Array.Reverse(temp);
            _writer.Write(BitConverter.ToSingle(temp, 0));
        }

        public float ReadFloat()
        {
            float retValue = 0;
            try
            {
                byte[] temp = BitConverter.GetBytes(_reader.ReadSingle());
                Array.Reverse(temp);
                retValue = BitConverter.ToSingle(temp, 0);
            }
            catch (System.Exception e)
            {
                LogError("DataBuffer ReadFloat error", e);
            }
            return retValue;
        }

        public void WriteDouble(double v)
        {
            byte[] temp = BitConverter.GetBytes(v);
            Array.Reverse(temp);
            _writer.Write(BitConverter.ToDouble(temp, 0));
        }

        public double ReadDouble()
        {
            double retValue = 0;
            try
            {
                byte[] temp = BitConverter.GetBytes(_reader.ReadDouble());
                Array.Reverse(temp);
                retValue = BitConverter.ToDouble(temp, 0);
            }
            catch (System.Exception e)
            {
                LogError("DataBuffer ReadDouble error", e);
            }
            return retValue;
        }

        public void WriteString(string v)
        {
            byte[] bytes = Encoding.UTF8.GetBytes(v);
            _writer.Write((uint)bytes.Length);
            _writer.Write(bytes);
        }

        public string ReadString()
        {
            string retValue = string.Empty;
            try
            {
                int len = (int)ReadUInt();
                byte[] buffer = new byte[len];
                if (len > (_reader.BaseStream.Length - _reader.BaseStream.Position))
                    throw new System.Exception($"out of range, stream pos:{_reader.BaseStream.Position} len:{_reader.BaseStream.Length} need len:{len}");
                buffer = _reader.ReadBytes(len);
                retValue = Encoding.UTF8.GetString(buffer);
            }
            catch (System.Exception e)
            {
                LogError("DataBuffer ReadString error", e);
            }
            return retValue;
        }

        public void ReWriteBytes(byte[] v)
        {
            this.Clear();
            _writer.Write(v);
        }
        public void WriteBytes(byte[] v)
        {
            _writer.Write(v);
        }

        public void WriteBytes(byte[] v, int index, int len)
        {
            _writer.Write(v, 0, len);
        }

        public byte[] ReadBytes(int len)
        {
            byte[] retValue = null;
            try
            {
                retValue = _reader.ReadBytes(len);
            }
            catch (System.Exception e)
            {
                LogError("DataBuffer ReadBytes error", e);
            }
            return retValue;
        }

        public byte[] ReadBytes()
        {
            return _reader.ReadBytes(RemainLen());
        }

        public byte[] ToBytes()
        {
            _writer.Flush();
            return _stream.ToArray();
        }

        public void Flush()
        {
            _writer.Flush();
        }

        public void PrintBytes()
        {
            string returnStr = string.Empty;
            byte[] a = _stream.ToArray();
            for (int i = 0; i < a.Length; ++i)
            {
                returnStr += a[i].ToString("X2");
            }
            Log.Info(returnStr);
        }

        public string GetString()
        {
            return _reader.ReadString();
        }

        public void Clear()
        {
            if (0 == GetLength())
                return;
                
            SetLength(0);
            _stream.Flush();
            _stream.Seek(0, SeekOrigin.Begin);
        }

        void LogError(string log, System.Exception e)
        {
            Seek(0, SeekOrigin.Begin);
            Log.Error($"{log}: pos={_stream.Position} len={_stream.Length}\n e=" + e);
        }
    }
}